#!/usr/bin/tclsh

lappend auto_path [file dirname [info script]]
package require ossltest
if {$argc==2} {
	switch -exact -- [lindex $argv 0]  {
	-serverconf {
		set test::server_conf [lindex $argv 1]
		set test::suffix "-clt"
	} 
	-clientconf {
		set test::client_conf [lindex $argv 1]
		set test::suffix "-srv"
	}
	default {
		puts stderr "invalid command line"
		exit 1;
	}
	}
} elseif $argc {
	puts stderr "invalid command line"
	exit 1
}	

array set protos {
	TLSv1 -tls1
	TLSv1.1 -tls1_1
	TLSv1.2 -tls1_2
}

cd $::test::dir

start_tests "Тесты на SSL-соединение между s_client и s_server"

if {[info exists env(ALG_LIST)]} {
	set alg_list $env(ALG_LIST)
} else {
	switch -exact [engine_name] {
		"ccore" {set alg_list {rsa:1024 gost2001:XA gost2012_256:XA gost2012_512:A}}
		"open" {set alg_list {rsa:1024 gost2001:XA gost2012_256:XA gost2012_512:A}}
	}
}

array set suites {
rsa:1024 {ECDHE-RSA-AES256-SHA@SECLEVEL=0}
gost2001:XA {GOST2001-GOST89-GOST89 GOST2001-NULL-GOST94@SECLEVEL=0 LEGACY-GOST2012-GOST8912-GOST8912 IANA-GOST2012-GOST8912-GOST8912 GOST2012-NULL-GOST12@SECLEVEL=0}
gost2012_256:XA {LEGACY-GOST2012-GOST8912-GOST8912 GOST2012-NULL-GOST12@SECLEVEL=0}
gost2012_512:A {LEGACY-GOST2012-GOST8912-GOST8912 GOST2012-NULL-GOST12@SECLEVEL=0}
}

#
# Incompatible cipher suites
#
array set badsuites {
gost2012_256:XA {GOST2001-GOST89-GOST89 GOST2001-NULL-GOST94@SECLEVEL=0} 
gost2012_512:A {GOST2001-GOST89-GOST89 GOST2001-NULL-GOST94@SECLEVEL=0}
}

#
# Default cipher suite negotiated for algorithm
#
array set defsuite {
rsa:1024 ECDHE-RSA-AES256-SHA
#gost94:XA GOST94-GOST89-GOST89
gost2001:XA GOST2012-GOST8912-GOST8912
gost2012_256:XA LEGACY-GOST2012-GOST8912-GOST8912
gost2012_512:A LEGACY-GOST2012-GOST8912-GOST8912
}

array set defsuite_12 {
rsa:1024 ECDHE-RSA-AES256-GCM-SHA384
#gost94:XA GOST94-GOST89-GOST89
gost2001:XA LEGACY-GOST2012-GOST8912-GOST8912
gost2012_256:XA GOST2012-MAGMA-MAGMAOMAC
gost2012_512:A GOST2012-MAGMA-MAGMAOMAC
}

set proto_list {"TLSv1" "TLSv1.1" "TLSv1.2"}

if {![file exists sslCA/cacert.pem]} {
	makeCA sslCA rsa
} else {
	set ::test::ca sslCA	
}

test -skip {[file exist localhost_rsa/cert.pem]} \
	"Создаем серверный сертификат rsa" {
	makeRegisteredUser localhost_rsa rsa:1024 CN localhost
} 0 1

foreach alg $alg_list {
	set alg_fn [string map {":" "_"} $alg]
	
	test -skip {[file exist localhost_$alg_fn/cert.pem]} \
		"Создаем серверный сертификат $alg" {
		makeRegisteredUser localhost_$alg_fn $alg CN localhost OU $alg_fn
	} 0 1

	test -skip {[file exists ssl_user_$alg_fn/cert.pem]} \
		"Создаем клиентский сертификат $alg" {
		makeRegisteredUser ssl_user_$alg_fn $alg CN ssl_user OU $alg_fn
	} 0 1
}

foreach alg {gost2001:B gost2012_256:B gost2012_512:B} {
	set alg_fn [string map {":" "_"} $alg]
	test -skip {[file exists ssl_user_$alg_fn/cert.pem]} \
		"Создаем клиентский сертификат $alg" {
		makeRegisteredUser ssl_user_$alg_fn $alg CN ssl_user OU $alg_fn
	} 0 1
}


foreach proto $proto_list {
	foreach alg $alg_list {
		set alg_fn [string map {":" "_"} $alg]

		if {[string match *2012* $alg]} {
			foreach suite $badsuites($alg) {

				test "Incompatible suite $alg $suite $proto" {
					set list [client_server [list -connect localhost:4433 \
						-CAfile $::test::ca/cacert.pem \
						-verify_return_error -verify 1 -state -cipher $suite] \
						[list -www -cert localhost_$alg_fn/cert.pem \
						-key localhost_$alg_fn/seckey.pem -cipher $suite \
						$protos($proto)] {}] 
					list [lindex $list 2] [grep "^New," [lindex $list 0]]
				} 0 [list 1 "New, (NONE), Cipher is (NONE)\n"]
			}
		}

		foreach suite $suites($alg) {
			set raw_name [lindex [split $suite @] 0]

			if {![string equal $proto "TLSv1.2"] && [string match *OMAC* $suite]} {
				continue
			}

			if {[string equal $proto "TLSv1.2"] && [string match *OMAC* $suite]} {
				set expected_proto "TLSv1.2"
			} else {
				set expected_proto "TLSv1.0"
			}

			test "Запуск сервера $suite $proto" {
				set f [open_server [list -cert localhost_$alg_fn/cert.pem \
					-key localhost_$alg_fn/seckey.pem -cipher $suite \
					$protos($proto)]]
				stop_server $f
				foreach {out err status} [stop $f] break	
				log "SERVER OUTPUT:\n$out\n----"
				log "SERVER STDERR:\n$err\n----"
				log "SERVER EXIT CODE: $status"
				grep "ACCEPT" $out
			} 0 "ACCEPT\n"
			log $errorInfo
			flush [test_log]

			test "Корректный хэндшейк $suite $proto" {
				set list [client_server [list -connect localhost:4433 \
					-CAfile $::test::ca/cacert.pem -verify_return_error \
					-verify 1 -state -cipher $suite ] \
					[list -www -cert localhost_$alg_fn/cert.pem \
					-key localhost_$alg_fn/seckey.pem \
					-cipher $suite $protos($proto)] {}] 
				if {[regexp -lineanchor \
				{^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \
				[lindex $list 0] -> result_proto result_cipher]} {
					list [lindex $list 2] $result_proto $result_cipher
				} else {
					lindex $list 1
				}	
			} 0 [list 0 $proto $raw_name]


			test "Несовпадающий шиферсьют DHE-RSA-AES256-SHA $proto" {
				set list [client_server [list -connect localhost:4433 \
					-CAfile $::test::ca/cacert.pem -verify_return_error \
					-verify 1 -state -cipher $suite] \
					[list -www -cert localhost_$alg_fn/cert.pem \
					-key localhost_$alg_fn/seckey.pem \
					-cipher DHE-RSA-AES256-SHA $protos($proto)] {}] 
				list [lindex $list 2] [grep ":fatal:" [lindex $list 1]]
			} 0 [list 1 "SSL3 alert read:fatal:handshake failure
"]

			test "Получение странички $suite $proto" {
				set list [client_server [list -connect localhost:4433 \
					-CAfile $::test::ca/cacert.pem -verify_return_error \
					-verify 1 -state -cipher $suite -ign_eof] \
					[list -www -cert localhost_$alg_fn/cert.pem \
					-key localhost_$alg_fn/seckey.pem -cipher $suite \
					$protos($proto)] "GET /\n\n"] 
				grep "^New," [lindex $list 0]
			} 0 "New, $expected_proto, Cipher is $raw_name\nNew, $expected_proto, Cipher is $raw_name\n"

			if {![string match "*-NULL-*" $suite]} {

				test "Сервер поддерживающий много шиферсьютов $proto" {
					set list [client_server [list -connect localhost:4433 \
						-CAfile $::test::ca/cacert.pem -verify_return_error \
						-verify 1 -state -cipher $suite] \
						[list -www -cert localhost_$alg_fn/cert.pem \
						-key localhost_$alg_fn/seckey.pem $protos($proto) -cipher ALL@SECLEVEL=0] {}] 
					if {[regexp -lineanchor \
					{^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \
					[lindex $list 0] -> result_proto result_cipher]} {
						list [lindex $list 2] $result_proto $result_cipher
					} else {
						lindex $list 1
					}	
				} 0 [list 0 $proto $raw_name]


				test "Сервер c несколькими алгоритмами, клиент $suite $proto" {
					set list [client_server [list -connect localhost:4433 \
						-CAfile $::test::ca/cacert.pem -verify_return_error \
						-verify 1 -state -cipher $suite] \
						[list -www -cert localhost_rsa/cert.pem \
						-key localhost_rsa/seckey.pem \
						-dcert localhost_$alg_fn/cert.pem \
						-dkey localhost_$alg_fn/seckey.pem $protos($proto) -cipher ALL@SECLEVEL=0] {}] 
					if {[regexp -lineanchor \
					{^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \
					[lindex $list 0] -> result_proto result_cipher]} {
						list [lindex $list 2] $result_proto $result_cipher
					} else {
						lindex $list 1
					}	
				} 0 [list 0 $proto $raw_name]

			}

			test "Сервер c несколькими алгоритмами, клиент AES256-SHA $proto" {
				set list [client_server [list -connect localhost:4433 \
					-CAfile $::test::ca/cacert.pem -verify_return_error \
					-verify 1 -state -cipher AES256-SHA@SECLEVEL=0] \
					[list -www -cert localhost_rsa/cert.pem \
					-key localhost_rsa/seckey.pem \
					-dcert localhost_$alg_fn/cert.pem \
					-dkey localhost_$alg_fn/seckey.pem $protos($proto) -cipher ALL@SECLEVEL=0] {}] 
				if {[regexp -lineanchor \
				{^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \
				[lindex $list 0] -> result_proto result_cipher]} {
					list [lindex $list 2] $result_proto $result_cipher
				} else {
					lindex $list 1
				}	
			} 0 [list 0 $proto AES256-SHA]



			if {[string match *gost* $alg]} {
				set alg_cli_list [list $alg gost2001:B gost2012_256:B gost2012_512:B]
			} else {
				set alg_cli_list $alg
			}

			foreach alg_cli $alg_cli_list { 
				set alg_cli_fn [string map {":" "_"} $alg_cli]

				test "Сервер $alg, клиент с сертификатом $alg_cli $proto" {
					set list [client_server [list -connect localhost:4433\
						-CAfile $::test::ca/cacert.pem -verify_return_error \
						-verify 1 -state -cert ssl_user_$alg_cli_fn/cert.pem \
						-key ssl_user_$alg_cli_fn/seckey.pem -cipher $suite \
						-ign_eof]\
						[list -cert localhost_$alg_fn/cert.pem \
						-key localhost_$alg_fn/seckey.pem -verify_return_error\
						-Verify 3 -www -CAfile $::test::ca/cacert.pem \
						-cipher $suite $protos($proto)] "GET /\n"]
					list [lindex $list 2] [grep "^New," [lindex $list 0]]
				} 0 [list 0 [string repeat "New, $expected_proto, Cipher is $raw_name\n" 2]]

			}

		}

		if {[string equal $proto "TLSv1.2"]} {
			set etalon $defsuite_12($alg)
		} else {
			set etalon $defsuite($alg)
		}

		if {[string equal $proto "TLSv1.2"] && ![string match *2001* $alg]} {
			set expected_proto "TLSv1.2"
		} else {
			set expected_proto "TLSv1.0"
		}
if {0} {
		test "Умолчательный хендшейк с ключами $alg $proto" {
			set list [client_server [list -connect localhost:4433\
				-CAfile $::test::ca/cacert.pem -verify_return_error -verify 1\
				-state -ign_eof]\
				[list -www -cert localhost_$alg_fn/cert.pem\
				-key localhost_$alg_fn/seckey.pem $protos($proto)] "GET /\n"]
			if {[regexp -lineanchor \
			{^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \
			[lindex $list 0] -> result_proto result_cipher]} {
				list [lindex $list 2] $result_proto $result_cipher
			} else {
				lindex $list 1
			}	
		} 0 [list 0 $proto $etalon]

		test "Умолчательный хендшейк с клиентской аутентификацией $alg $proto" {
			set list [client_server [list -connect localhost:4433\
				-CAfile $::test::ca/cacert.pem -verify_return_error \
				-verify 1 -state -cert ssl_user_$alg_fn/cert.pem \
				-key ssl_user_$alg_fn/seckey.pem -ign_eof]\
				[list -cert localhost_$alg_fn/cert.pem \
				-key localhost_$alg_fn/seckey.pem -verify_return_error\
				-Verify 3 -www -CAfile $::test::ca/cacert.pem $protos($proto)] \
				"GET /\n"]
			list [lindex $list 2] [grep "^New," [lindex $list 0]]
		} 0 [list 0 [string repeat "New, $expected_proto, Cipher is $etalon\n" 2]]
}; # if {0}
	}
}

end_tests
